import { EventDispatcher } from "../../base/event/EventDispatcher";

let LinkEvent = {
    APPEND: "Link.APPEND",
    SUBTRACT: "Link.SUBTRACT",
    UPDATE: "Link.UPDATE"
}

/**
 * 模块化的数据链，方便数据扩展（组合优于继承）
 * 
 */
export abstract class Link<DataStruct> {
    private _event:EventDispatcher = new EventDispatcher();
    protected _linkName:string = "";
    protected _parent:Link<any>;
    protected _children:Map<string, Link<any>> = new Map();
    protected _data:DataStruct;

    constructor(linkName:string, data:DataStruct) {
        this._linkName = linkName;
        this._data = data;
    }
    
    /**
     * 获取父级
     */
    public get parent():Link<any> {
        return this._parent;
    }

    /**
     * 设置父级对象，值为null时，从父级移除。
     */
    public set parent(value:Link<any>) {
        if(this._parent){
            this._parent.subtract(this.linkName, false);
        }
        if(!!value){
            value.append(this);
        }
    }

    public get linkName():string {
        return this._linkName;
    }

    /**
     * 所有下级
     */
    public get children():Link<any>[] {
        return Array.from(this._children.values());
    }

    /**
     * 获取指定名称子项，更便捷的方式 linkObject.name
     * @param name 子项名称
     * @returns 
     */
    public child<T extends Link<any>>(name:string):T {
        return this._children.get(name) as T;
    }

    /**
     * 下级数量
     */
    public get childrenCount():number {
        return this._children.size;
    }

    /**
     * 链接下级
     * @param types 一个或多个下一级的Class
     */
    public append(...types:{new ():Link<any>}[]):Link<any>;
    /**
     * 链接下级
     * @param links 一个或多个下一级的对象
     */
    public append(...links:Link<any>[]):Link<any>;
    public append(...args:{new ():Link<any>}[]|Link<any>[]):Link<any> {
        for (let i = 0; i < args.length; i++) {
            const item = args[i];
            if(item instanceof Link){
                this._appendChild(item);
            }else{
                let target:Link<any> = new item();
                this._appendChild(target);
            }
        }
        return this;
    }

    private _appendChild(target:Link<any>):void {
        Object.defineProperty(this, target.linkName, {
            get:()=>{
                return this._children.get(target.linkName);
            },
            set:(value:Link<any>|null)=>{
                if(!!value){
                    if(this[value.linkName]){
                        console.log(`${this.linkName}Link中已存在${value.linkName}的Link`);
                    }
                    if(value.linkName != target.linkName){
                        throw new Error(`目标Link的name属性必须为${target.linkName}`);
                    }
                    value._parent = this;
                    this._onEvents(value);
                    this._children.set(value.linkName, value);
                    this._event.event(LinkEvent.APPEND, [value, this]);
                }else{
                    this.subtract(target.linkName, false);
                }
            }}
        );
        this[target.linkName] = target;
    }

    private _onEvents(child:Link<any>):void {
        child.onChildAppended(this, this._onChildAppend);
        child.onChildSubtracted(this, this._onChildSubtract);
        child.onUpdated(this, this._onChildUpdate);
    }

    private _offEvents(child:Link<any>):void {
        child.offChildAppended(this, this._onChildAppend);
        child.offChildSubtracted(this, this._onChildSubtract);
        child.offUpdated(this, this._onChildUpdate);
    }

    protected _onChildAppend(child:Link<any>, parent:Link<any>):void {
        this._event.event(LinkEvent.APPEND, [child, parent]);
    }

    protected _onChildSubtract(child:Link<any>, parent:Link<any>):void {
        this._event.event(LinkEvent.SUBTRACT, [child, parent]);
    }

    protected _onChildUpdate(name:string, newValue:any, target:Link<any>, oldValue:any):void {
        this._event.event(LinkEvent.UPDATE, [`${target.linkName}.${name}`, newValue, target, oldValue]);
    }

    /**
     * 移除下级，更快捷的方式linkObject.name = null;
     * @param name 下级名称
     * @param destory 是否销毁下级，默认为true
     */
    public subtract(name:string, destory?:boolean):boolean;
    /**
     * 移除下级，更快捷的方式linkObject.name = null;或者target.parent = null;
     * @param target 下级对象
     * @param destory 是否销毁下级，默认为true
     */
    public subtract(target:Link<any>, destory?:boolean):boolean;
    public subtract(target:Link<any>|string, destory:boolean=true):boolean {
        if(typeof target === "string"){
            if(!this[target]){
                throw new Error(`${target}不存在。`);
            }
            return this.subtract(this[target]);
        }else{
            if(this._children.has(target.linkName)){
                this._children.delete(target.linkName);
                this._offEvents(target);
                delete this[target.linkName];
                target._parent = null;
                this._event.event(LinkEvent.SUBTRACT, [target, this]);
                if(destory){
                    target.destory();
                }
                return true;
            }
        }
        return false;
    }

    /**
     * 数据更新后的通知
     * @param newValue 新设置的值
     * @param oldValue 原先的值
     */
    protected updated<T>(name:string, newValue:T, oldValue:T, ...args:any[]):void {
        this._event.event(LinkEvent.UPDATE, [name, newValue, this, oldValue, ...args]);
    }

    /**
     * 重置数据
     * @param children 是否同时重置所有子项的数据
     */
    public reset(children:boolean=true):void {
        this._restSelfData();
        children && this.children.forEach((child)=>{
            child._restSelfData();
        });
    }

    /**
     * 重置自身数据
     */
    protected abstract _restSelfData():void;

    /**
     * 销毁
     */
    public destory():void {
        let children:Link<any>[] = this.children;
        for (const child of children) {
            this.subtract(child, true);
        }
        this._children.clear();
        this._children = null;
        this._event.offAllEvent();
        this._event = null;
        this.parent = null;
        this._data = null;
    }

    /**
     * 解析字符串或序列化Object并填充到自身及所有子项数据
     * @param value 
     */
    public parse(value:string|Object):void {
        let obj = typeof value === "string" ? JSON.parse(value) : value;
        if(!obj.data){
            throw new Error("解析失败, data数据缺失")
        }
        if(!obj.child){
            throw new Error("解析失败, child数据缺失")
        }
        this._data = obj.data;
        for (const key in obj.child) {
            if (Object.prototype.hasOwnProperty.call(obj.child, key)) {
                if(key != "_data"){
                    let child = <Link<any>>this[key];
                    if(child){
                        child.parse(obj.child[key]);
                    }else{
                        console.warn(`未找到名称为：${key}的child Link`)
                    }
                }else{
                    this._data = obj.child[key];
                }
            }
        }
    }
    
    /**
         * 转换自身及所有子项数据为字符串。字符串格式：`{data:{key:value},child:{childname:{data:key:value,child:{...}}, ...}}`
         */
    public toString():string {
        let childData = {};
        let children = this.children;
        for (const child of children) {
            childData[child.linkName] = child.toString();
        }
        return JSON.stringify({child:childData, data:this._data||{}});
    }

    public toObject():object {
        let childData = {};
        let children = this.children;
        for (const child of children) {
            childData[child.linkName] = child.toObject();
        }
        return {child:childData, data:this._data||{}};
    }

    ////////事件////////
    /**
     * 添加子项增加的监听
     * @param caller 
     * @param listener 
     * @param args 
     */
    public onChildAppended(caller: any, listener: (child?:Link<any>, parent?:Link<any>)=>void, ...args:any[]):void{
        this._event.on(LinkEvent.APPEND, caller, listener, args);
    }
    /**
     * 移除子项添加监听
     * @param caller 
     * @param listener 
     */
    public offChildAppended(caller: any, listener: (child?:Link<any>, parent?:Link<any>)=>void):void{
        this._event.off(LinkEvent.APPEND, caller, listener);
    }

    /**
     * 添加移除子项的监听
     * @param caller 
     * @param listener 
     * @param args 
     */
    public onChildSubtracted(caller: any, listener: (child?:Link<any>, parent?:Link<any>)=>void, ...args:any[]):void{
        this._event.on(LinkEvent.SUBTRACT, caller, listener, args);
    }

    /**
     * 移除子项移除的监听
     * @param caller 
     * @param listener 
     */
    public offChildSubtracted(caller: any, listener: (child?:Link<any>, parent?:Link<any>)=>void):void{
        this._event.off(LinkEvent.SUBTRACT, caller, listener);
    }

    /**
     * 添加更新（包括子项）的监听
     * @param caller 
     * @param listener 
     * @param args 
     */
    public onUpdated(caller: any, listener: (name?:string, newValue?:any, target?:Link<any>, oldValue?:any, ...args:any[])=>void, ...args:any[]):void{
        this._event.on(LinkEvent.UPDATE, caller, listener, args);
    }

    /**
     * 移除更新的监听
     * @param caller 
     * @param listener 
     */
    public offUpdated(caller: any, listener: (name?:string, newValue?:any, target?:Link<any>, oldValue?:any)=>void):void{
        this._event.off(LinkEvent.UPDATE, caller, listener);
    }

    /**
     * 移除监听者下的所有监听
     * @param caller 
     */
    public offAllByCaller(caller: any):void{
        this._event.offByCaller(caller);
    }

    /**
     * 移除所有监听
     */
    public offAll():void{
        this._event.offAllEvent();
    }
}
